from typing import AbstractSet, Generic, Optional, Type, TypeVar, Callable, Set, List
import inspect

K = TypeVar("K")
C = TypeVar("C")
A = TypeVar("A")


class Registry(Generic[K, C, A]):
    class Priority:
        MAXIMUM = 0
        HIGH = 25
        MEDIUM = 50
        LOW = 75
        MINIMUM = 100

    def __init__(self):
        self.entries = set()

    def get(self, key: K, *args) -> Optional[C]:
        """
        Retrieve an instance of C based on the key and provided arguments.
        """
        factory = self.get_factory(key)
        if factory:
            return factory.instantiate(list(args))
        return None

    def get_factory(self, key: K) -> Optional["Factory"]:
        """
        Get the factory associated with the provided key.
        """
        entry = self.find(key)
        if entry:
            return entry.factory
        return None

    def find(self, key: K) -> Optional["Entry"]:
        """
        Find the entry associated with the provided key.
        """
        return self.find_entry(key)

    def find_by_id(self, id: str) -> Optional["Entry"]:
        """
        Find the entry by its id.
        """
        for entry in self.entries:
            if entry.id == id:
                return entry
        return None

    def install(self, clazz: Type[C], annotation: A):
        """
        Install a new matcher class with its annotation.
        """
        entry = self.new_entry(clazz, annotation)
        self.entries.add(entry)

    def clear(self):
        """
        Clear the registry.
        """
        self.entries.clear()

    def new_entry(self, clazz: Type[C], annotation: A) -> "Entry":
        """
        Create a new registry entry.
        """
        raise NotImplementedError()

    def find_entry(self, key: K) -> Optional["Entry"]:
        """
        Find the entry that handles the given key.
        """
        for entry in self.entries:
            if entry.handle(key):
                return entry
        return None

    def find_by_class(self, clazz: Type[C]) -> Optional["Entry"]:
        """
        Find the entry by its class.
        """
        for entry in self.entries:
            if entry.clazz == clazz:
                return entry
        return None

    def get_entries(self) -> AbstractSet["Entry"]:
        """
        Get all entries in the registry.
        """
        return frozenset(self.entries)

class Entry:
    def __init__(self, id: str, clazz: Type[C], factory: "Factory", priority: int):
        self.id = id
        self.clazz = clazz
        self.factory = factory
        self.priority = priority

    def instantiate(self, args: List) -> Optional[C]:
        """
        Instantiate an object of type C with the provided arguments.
        """
        return self.factory.new_instance(args)

    def handle(self, key: K) -> bool:
        """
        Determines if this entry handles the given key.
        """
        raise NotImplementedError()

    def __str__(self):
        return self.id

def default_factory(self, clazz: Type[C], *signature) -> "Factory":
    """
    Create a default factory for the given class and signature.
    """
    try:
        ctor = clazz.__init__
        return Factory(clazz, ctor)
    except Exception as e:
        raise RuntimeError(f"Constructor for {clazz.__name__} not found") from e


class Factory(Generic[C]):
    def __init__(self, clazz: Type[C], constructor: Callable):
        self.clazz = clazz
        self.constructor = constructor

    def new_instance(self, args: List) -> Optional[C]:
        """
        Create a new instance of C using the constructor with the provided arguments.
        """
        try:
            return self.constructor(*args)
        except Exception as e:
            return None

    def instantiate(self, args: List) -> Optional[C]:
        """
        Instantiate an object of type C with the provided arguments.
        """
        return self.new_instance(args)
